#!/usr/bin/perl

###################################################################################### 
#                                                                                    #
# check_cpnr_logs                                                                    #
#                                                                                    #
# Check Cisco Prime Netwrok Registrar log files for specific errors like "no Scopes" #
#                                                                                    #
# Create by kresimir.petrovic@gmail.com for moniring different output in CNR         #
# log files                                                                          #
#                                                                                    #
# Nagios::Plugin perl module is required                                             #
#                                                                                    #
###################################################################################### 

package main;

our $VERSION = '1.0.5';

use strict;
use POSIX;
use Data::Dumper;

use Nagios::Plugin;
use Nagios::Plugin::Getopt;
use Nagios::Plugin::Threshold;

use vars qw(
			$debug
 			@match_and_ignore
			%error_mapping
);

%error_mapping = (	'OK' =>0,
					'WARNING' => 1,
					'CRITICAL' => 2);


# Match and ignore array of arrays:
#	First item is Error levels
#	Second item is error text to be displayed in monitor output
#	Match based on second item in array, skips if third item present (show me no scopes for everything except 192.168.x.x)
# 	Standard regexp can be used, parantheses are mandatory they are used to count number of occurrences for alarm deduplication
#   Examples below:
#@match_and_ignore = (	['CRITICAL', 'No scopes for: ', 'no Scopes for source network \'(.*)\'', '192\.168\.1\.6|192\.168\.1\.5']);

#@match_and_ignore = (	['CRITICAL', 'No Scopes for: ', 'no Scopes for source network \'(.*)\'', '192\.168\.1\.\d+'],
#						['WARNING', 'Lease Granted to: ', 'Lease granted to Host: \'(\w+)\' ', 'android|Android'],
#						['OK', 'Inform response req: ','Client Host: \'(A\w+)\'.*requested an INFORM response', '^$'],
#						['WARNING', 'Lease Granted to: ', 'Lease granted to Host: \'(hakan)\' ', '^$']);

#@match_and_ignore = (	['CRITICAL', 'No Scopes for: ', 'no Scopes for source network \'(.*)\'', '^$'] );
@match_and_ignore = (	['CRITICAL', 'No Scopes for: ', 'no Scopes for source network \'(.*)\'', '192\.168\.1\.\d+'] );

if ( !caller ) {
    main();
}

sub main {
	
	# %matched: Hash of hashes will store search results, first key would be error text
	my (%matched);
	my ($plugin, $arg_opts);
	my ($out_text, $error_level);
	my ($filesize);
	
	$plugin = Nagios::Plugin->new( shortname => 'CPNR logs'); 
	$arg_opts = parse_nagios_plugin_options();	
	$debug = $arg_opts->debug();


	$filesize = -s $arg_opts->file() or die "Can't determine file size for: \"".$arg_opts->file()."\"\n";
	$filesize = sprintf '%.2f', $filesize/1024/1024;

	print "[DEBUG] Filesize ".$arg_opts->file().": ".$filesize."MB\n" if ( $debug );;

	if ($filesize > $arg_opts->filesize())
	{
		$plugin->nagios_exit( WARNING, "Log file size to big: $filesize MB, limit: ".$arg_opts->filesize()."MB" );
	}

	%matched = parse_log_file($arg_opts->file());

	# Populate errorlevels for each matching target
	foreach my $target (@match_and_ignore)
	{
		$matched{$target->[1]}{'__ERROR_LEVEL__'} = $target->[0];
	}
	$error_level='UNKNOWN';

	
	print "[DEBUG] ".Dumper(\%matched) if ( $debug );


	# Sort keys so that info from Error is comming before info from warning
	($error_level, $out_text) = gen_error_output(\%matched);

	$plugin->nagios_exit( $error_level, $out_text );
}

sub parse_nagios_plugin_options()
{
	my ($options);

	$options = Nagios::Plugin::Getopt->new( 
		usage => "Usage: %s [ -v|--verbose ]  [-F <file>] [ -S|--filesize <filesize>] [-t <timeout> ] [ -c|--critical=<critical threshold> ] [ -w|--warning=<warning threshold> ]  ",
		version => $VERSION,
		blurb   => 'Monitor CPNR logs for various errors. Using @match_and_ignore array, different watching targets can be specified using regexp. By default monitoring for \"no Scopes\"',
		extra   => 'Created by: Kresimir Petrovic kresimir.petrovic@gmail.com',
#		url     => $url,
		timeout => 15
	);

	$options->arg(
		spec     => 'file|F=s',
		help     => 'log file',
		required => 1,
     ); 

	$options->arg(
		spec     => 'filesize|S=n',
		help     => 'file size',
		required => 0,
		default  => 100
     ); 

	$options->arg(
		spec     => 'critical|c=s',
		help     => 'critical number of day',
		required => 0,
     ); 

    $options->arg(
		spec     => 'warning|w=s',
		help     => 'warning number of day',
		required => 0,
    );

    $options->arg(
		spec     => 'debug',
		help     => 'debugging output',
		required => 0,
    );
     
    $options->arg(
		spec     => 'noperfdata|n',
		help     => 'no perfdata to output',
		required => 0,
    );

    $options->getopts();
	return $options;
}
sub parse_log_file()
{
	my (%result, $filename);
	$filename = $_[0];

	open (FILE, $filename ) or die "Can't open file: \"$filename\"\n";
	while (<FILE>)
	{
		chomp;

		foreach my $target (@match_and_ignore)
		{
			if (m/$target->[2]/ and !m/$target->[3]/)
			{
				$result{$target->[1]}{$1}++;
				print "[DEBUG] Found $target->[1] $result{$target->[1]}{$1}# $1\n" if ( $debug );
			}
		}
	}
	close FILE;
	return %result;
}

sub gen_error_output()
{
	my ($error_level, $out_text, %search_result);

	%search_result=%{$_[0]};

	foreach my $target (sort { $error_mapping{$search_result{$b}->{'__ERROR_LEVEL__'}} cmp $error_mapping{$search_result{$a}->{'__ERROR_LEVEL__'}} } keys %search_result)
	{
		# Parse target by target
		$out_text=$out_text.$target;
		foreach my $item (keys(%{$search_result{$target}}))
		{
			# Check current error level. Raise if necessary.
			if ($item eq '__ERROR_LEVEL__')
			{
				$error_level = $search_result{$target}{$item} if ($error_mapping{$search_result{$target}{$item}} > $error_mapping{$error_level});
			}
			# Append error text in format itemm #num_repeat
			else
			{
				$out_text=$out_text.$item." #".$search_result{$target}{$item}.", ";
			}
		}
	}
	return ($error_level, $out_text);
}
